def tensor_operations():
    sample_code_one = """
import torch

# 张量tensor，类似于Numpy的ndarray，是pytorch框架运算的基本单元，
# 具有多维性、能存储不同类型的数据，并且通过.to(设备)可以在
# CPU和GPU之间转换，通过requirs_grad=True开启自动梯度计算。

# 1. 创建张量--------------------------------------------------
print(torch.tensor(666))
print(torch.tensor([11, 22, 33]))
print(torch.tensor([[1, 2], [3, 4]]))
print(torch.empty(3))   # 创建size=3的空张量，值为随机值可能为0也可能是垃圾值
print(torch.empty((2, 3))) # size=(2, 3)
print(torch.zeros(1, 2))    # 全0数组
print(torch.ones(2, 3))
torch.manual_seed(100)      # 随机数种子，作用是【复现】
print(torch.rand(2, 1))     # 返回一个由区间[0,1)上均匀分布的随机数填充的张量
print(torch.randn(2, 3))    # 返回一个张量，其中充满来自均值为0，方差为1的正态分布（也称为标准正态分布）的随机数
print(torch.normal(mean=-1, std=2, size=(2,3)))   # 生成指定均值和标准差的正态分布随机数


# 2. 数据类型----------------------------------------------------
t = torch.ones(2, 3)
print(t.dtype)      # torch.float32
t = torch.ones(2, 3, dtype=torch.int8)
print(t.dtype)      # torch.int8
t = t.float()
print(t.dtype)      # torch.float32
t = t.to(torch.int16)
print(t.dtype)      # torch.int16


# 3. 访问张量------------------------------------------------------
t = torch.tensor([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
    [10, 11, 12]
])
# 通过下标索引、切片
print(t[0])     # tensor([1, 2, 3])
print(t[0, 2])  # tensor(3)
''''
numpy(张量)   以NumPy数组的形式返回张量。
item(张量)    将此张量的值作为标准Python数字返回。这只适用于只有一个元素的张量。
'''
print(t[0, 2].numpy())  # 3     <class 'numpy.ndarray'>
print(t[0, 2].item())   # 3     <class 'int'>
print(t[0:2])           # tensor([[1, 2, 3],[4, 5, 6]])
print(t[:, 1:3])        # tensor([[ 2,  3],[ 5,  6],[ 8,  9],[11, 12]])

# 4. 修改形状---------------------------------------------------------
'''
reshape()和view()都可以改变形状，但是要具有相同数据和数量。
区别：view()改变形状之后内存不会重新分配，一般来说我们优先选用reshape
>>> a = torch. randn(1, 2, 3, 4)
>>> a. size()
torch. Size([1, 2, 3, 4])
>>> b = a. transpose(1, 2)  # 交换二维和三维空间
>>> b. size()
torch. Size([1, 3, 2, 4])
>>> c = a. view(1, 3, 2, 4)  # 不改变张量在内存中的布局
>>> c. size()
torch. Size([1, 3, 2, 4])
>>> torch. equal(b, c)
False
'''
t = torch.arange(12)
print(t.shape)      # torch.Size([12])
print(t.reshape(2, 6).shape)    # torch.Size([2, 6])
print(t.reshape(-1, 3).shape)   # -1会自动计算某个维度的大小    torch.Size([4, 3])
print(t.reshape(-1, 2, 3).shape)    # torch.Size([2, 2, 3])
print(t.view(-1, 6).size())     # torch.Size([2, 6])

    """
    sample_code_two = """
# 5.维度变换----------------------------------------------------------
t = torch.ones(2, 3, 4) # 三维数组
'''
transpose()     只能交换两个维度
permute()       重新排列维度的顺序
'''
print(t.transpose(0, 2).shape)      # torch.Size([4, 3, 2])
print(t.permute(1, 2, 0).shape)                # torch.Size([3, 4, 2])
'''
squeeze()       挤压所有尺寸为1的维度，默认挤压所有，也可指定某个轴
unsqueeze()     扩展维度，指定在哪个轴之前扩展维度且尺寸为1，
expand()        将已有的尺寸为1的维度可以扩展为任意值
'''
t = torch.ones(2, 1, 3, 1, 5)
print(t.squeeze().shape)        # torch.Size([2, 3, 5])
print(t.squeeze(dim=1).shape)   # torch.Size([2, 3, 1, 5])
print(t.squeeze(dim=2).shape)   # torch.Size([2, 1, 3, 1, 5]) 若指定维度不为1，不报错，但也不挤压
print(torch.squeeze(t).shape)   # torch.Size([2, 3, 5])
print(t.unsqueeze(dim=0).shape) # torch.Size([1, 2, 1, 3, 1, 5])
print(t.expand(-1, 3, -1, 4, -1).shape) # torch.Size([2, 3, 3, 4, 5])



# 6.基本运算----------------------------------------------------------
t = torch.tensor([
    [1, 2, 3],
    [4, 5, 6]
])
# 张量 与 标量进行 + - * / // % ** 这些基本运算都是OK的
print(t + 1)
print(t ** 2)
# 张量 与 同等形状的张量 进行+ - * / // % ** 这些基本运算都是OK的
t1 = torch.tensor([
    [10, 20, 30],
    [40, 50, 60]
])
print(t + t1)
# 若张量与不同形状的张量进行运算呢？ 考虑广播机制
# 注意：广播机制不一定能广播

# 7.广播机制----------------------------------------------------------
'''
pytorch中的张量广播是一种机制，用于在执行元素级操作时自动拓展具有不同形状的张量，使他们具有兼容的形状。
要符合一定的条件：
1. 维度数量相同时，对于每个维度，两个张量的大小要么相等，要么其中一个张量的大小为1
    t1.shape = (2, 3)   t2.shape = (2, 1)   t3.shape = (1, 3)   t4.shape = (1, 2)
    t1 + t1 OK
    t1 + t3 OK
    t2 + t3 OK
    t1 + t4 ERROR
    t2 + t4 OK
2. 如果两个张量的维度数量不相同，将较小的张量的形状前面补1，直到维度数相同
    t5.shape = (2, 2, 3)
    t1 + t5 OK      t1先(1, 2, 3)  再(2, 2, 3) 
    t2 + t5 OK      t2先(1, 2, 1)  再(2, 2, 3)
    t4 + t5 ERROR
'''
t1 = torch.zeros(2, 3)          # 全0
t2 = torch.zeros(2, 1) + 1      # 全1
t3 = torch.zeros(1, 3) + 2      # 全2
t4 = torch.zeros(1, 2) + 3      # 全3
print(t1 + t2)
print(t1 + t3)
print(t2 + t3)
# print(t1 + t4) The size of tensor a (3) must match the size of tensor b (2) at non-singleton dimension 1
t5 = torch.zeros(2, 2, 3) + 4   # 全4
print(t1 + t5)
print(t2 + t5)


# 8.关系运算----------------------------------------------------------
'''
> < == >= <= !=     返回真值表，一般用作掩膜，用于后续的其他操作
equal()     判断两个张量的内容是否完全一致
allclose()  判断两个张量的内容是否近似，通过atol设置误差阈值
'''
x = torch.tensor([[1, 2], [3, 4]])
y = torch.tensor([[1, 2], [4, 4]])
print(x > y)    # tensor([[False, False],[False, False]])
print(x == y)   # tensor([[ True,  True],[False,  True]])
mask = x==y
x[mask] = 0
print(x)        # tensor([[0, 0],[3, 0]])
print(torch.equal(x, y))    # False
print(torch.allclose(x, y, atol=0.01))  # False
x = torch.tensor([[1, 2], [3, 4]]).float()
y = torch.tensor([[1, 2.001], [3.0001, 4]])
print(torch.allclose(x, y, atol=0.01))  # True


# 9.统计运算----------------------------------------------------------
'''
max()   min()   sum()   mean()  var()   std()   sqrt()  注意多维度的时候，指定dim进行统计
'''
x = torch.tensor([
    [1, 9, 3, 4],
    [2, 3, 7, 5],
    [3, 8, 5, 6]
])
print(x.max())      # tensor(9)
print(x.max(dim=0))
'''
torch.return_types.max(
values=tensor([3, 9, 7, 6]),
indices=tensor([2, 0, 1, 2]))
'''
print(x.max(dim=1))
'''
torch.return_types.max(
values=tensor([9, 7, 8]),
indices=tensor([1, 2, 1]))
'''
# 记住：对于多维张量，进行统计计算的时候，指定dim=2，则维度2消失
# eg: t.shape = (2, 1, 3, 4)        t.max(dim=2)返回的最大值的values.shape = (2,1,4)
print(torch.max(x, dim=1))


# 10.矩阵运算----------------------------------------------------------
x = torch.zeros(2, 3) + 4       # 全4
y = torch.zeros(3, 4) + 5       # 全5
'''
mm()        不会广播    
matmul()    会广播
@
bmm()       批次矩阵相乘
'''
print(torch.mm(x, y).shape)
print(torch.matmul(x, y).shape)
print((x @ y).shape)       # torch.Size([2, 4])
img = torch.ones(4, 30, 20)
w = torch.ones(4, 20, 5)
print(torch.bmm(img, w).shape) # torch.Size([4, 30, 5])


# 11.其他操作----------------------------------------------------------
'''
concatenate()       拼接
concat()            拼接
stack()             堆叠（升维）
chunk()             拆分
flatten()           展平
detach()            脱离计算图并克隆张量
'''
x = torch.ones(3, 2, 5)
y = torch.ones(3, 2, 5)
print(torch.concatenate((x, y)).shape)  # torch.Size([6, 2, 5])
print(torch.concat((x, y)).shape)   # torch.Size([6, 2, 5])
print(torch.concat((x, y), dim=1).shape)   # torch.Size([3, 4, 5])
print(torch.stack((x, y)).shape)    # torch.Size([2, 3, 2, 5])
print(torch.stack((x, y), dim=1).shape)    # torch.Size([3, 2, 2, 5])
print(torch.chunk(x, 3))    # 拆分成3块，每一块的大小为(1, 2, 5)
print(torch.chunk(x, 3, dim=2)) # 拆分为3块，每一块大小分别为(3,2,2) (3,2,2) (3,2,1)
print(torch.flatten(x).shape)   # torch.Size([30])
print(torch.flatten(x, start_dim=1).shape)    # torch.Size([3, 10])
# 因为pytorch会自动跟踪张量的操作，也就是说它是动态图，若想在执行期间脱离计算图并克隆张量使用detach()
z = x.detach()
print(torch.equal(z, x))        # True

    """
    return sample_code_one, sample_code_two


def model_save_load():
    sample_code_one = """
import torch
import torch.nn as nn

model = nn.Sequential(
    nn.Linear(4, 64),
    nn.ReLU(),
    nn.Linear(64, 128),
    nn.ReLU(),
    nn.Linear(128, 2)
)

# # 1. 保存模型的权重和其他参数
# torch.save(model.state_dict(), 'model_state_dict.pth')
#
# # 2. 保存整个模型
# torch.save(model, 'model.pth')

# 3. 加载模型
net = torch.load('model.pth', weights_only=False)
x = torch.rand(10, 4)
print(net(x))

# 4. 加载模型状态字典
# 状态字典中只保留了模型的权重和其它参数，未保留模型结构
# 你要去用的时候，得先有模型，再将状态字典赋给模型
model.load_state_dict(torch.load('model_state_dict.pth'))
    """
    return sample_code_one


def model_view():
    sample_code_one = """
import torch
import torch.nn as nn

net = nn.Sequential(
    nn.Linear(4, 64),
    nn.ReLU(),
    nn.Linear(64, 128),
    nn.ReLU(),
    nn.Linear(128, 2)
)

# 1. print()
print(net)

# 2. summary
from torchsummary import summary

summary(net, input_size=(4,), batch_size=10, device='cpu')

# 3. netron
# 3.1 终端输入 netron 到网址上查看  (直接查看，无连线)

# 3.2 将模型转换成脚本，再保存，查看有连线

script_model = torch.jit.script(net)
torch.jit.save(script_model, 'script_model.pth')

# 3.3 转换为通用格式 ONNX
x = torch.rand(10, 4)
torch.onnx.export(net, (x,), 'net.onnx')
    """
    return sample_code_one
